//@Author: cyl
//@File: main.go
//@Time: 2023/06/04 21:33:21
package main

import "fmt"

// 接口

// go语言中的接口有专用的关键字interface, 相等于python中的协议
// interface不仅仅是用于处理多态的, 它可以接受任意类型的数据类型, 有点类似void
// · 接口类型具体描述了一系列方法的集合，一个实现了这些方法的具体类型是这个接口类型的实例 ·
// · 接口一般以er结尾 ·

// 1. Sayer 接口
type Sayer interface {
	say()
}

// 2. Mover 接口
type Mover interface {
	move()
}

// 定义dog和cat两个结构体
type dog struct {
	Name string
}

type cat struct {
	Name string
}

// dog实现Sayer接口
func (d dog) say() {
	fmt.Println(d.Name + "汪汪汪")
}

// cat实现Sayer接口
func (c cat) say() {
	fmt.Println("喵喵喵")
}

// dog实现Mover接口
func (d dog) move() {
	fmt.Println(d.Name + "会动")
}

// cat实现Mover接口
func (c cat) move() {
	fmt.Println(c.Name + "会动")
}

// 3. 接口嵌套
type animal interface {
	Sayer
	Mover
}

// 4. 空接口实现方法
func show(a interface{}) {
	fmt.Printf("type:%T value:%v\n", a, a)
}

// 5. 空接口判断类型
func justifyType(x interface{}) {
	switch v := x.(type) {
	case string:
		fmt.Printf("x is a string, value is %v\n", v)
	case int:
		fmt.Printf("x is a int is %v\n", v)
	case bool:
		fmt.Printf("x is a bool is %v\n", v)
	default:
		fmt.Println("unsupport type!")
	}
}

func main() {
	// 一、fmt.Println()
	// 格式化输出函数: func Println(a ...interface{}) (n int, err error) 即为典型的接口

	// 二、接口基础
	var i, j, k interface{}
	citys := []string{"北京", "上海", "深圳"}
	age := 18
	str := "真漂亮"

	i = citys
	j = age
	k = str

	fmt.Println("i代表slice: ", i) // i代表slice:  [北京 上海 深圳]
	fmt.Println("j代表int: ", j)   // j代表int:  18
	fmt.Println("k代表str: ", k)   // k代表str:  真漂亮

	// 三、实现接口的条件: 见本文件11-30行代码

	// 四、接口类型变量能够存储所有实现了该接口的实例
	var x Sayer
	x = cat{}
	x.say() // 喵喵喵
	x = dog{}
	x.say() // 汪汪汪

	// 五、一个类型实现多个接口
	var y Mover
	var d = dog{
		Name: "旺财",
	}
	x = d
	y = d
	x.say()  // 旺财汪汪汪
	y.move() // 旺财会动

	// 六、接口嵌套
	var ani animal = cat{
		Name: "花花",
	}
	ani.move() // 花花会动
	ani.say()  // 喵喵喵

	// 七、空接口
	// 7.1 空接口作为函数参数
	show("小王子和娜扎要结婚了")
	// 7.2 使用空接口实现可以保存任意值的字典
	var stu = make(map[string]interface{})
	stu["name"] = "沙河娜扎"
	stu["age"] = 18
	stu["married"] = false
	fmt.Println(stu) // map[age:18 married:false name:沙河娜扎]

	// 八、数据类型断言
	// 想要判断空接口中的值这个时候就可以使用类型断言, 其语法格式：x.(T)
	// x：表示类型为interface{}的变量, T：表示断言x可能是的类型
	// 该语法返回两个参数, 第一个参数是x转化为T类型后的变量, 第二个值是一个布尔值, 若为true则表示断言成功, 为false则表示断言失败
	var myAssert interface{} = "hello, 沙河小王子"
	justifyType(myAssert) // x is a string, value is hello, 沙河小王子

	// 九、多态
	lowlevel := LowHumanLevel{
		Name:  "Bob",
		Level: 1,
	}
	highlevel := HighHumanLevel{
		Name:  "David",
		Level: 10,
	}
	DoAttack(&lowlevel)  // 我是 Bob, 我的等级是 1, 造成了100点伤害
	DoAttack(&highlevel) // 我是 David, 我的等级是 10, 造成了5000点伤害

	// 十、接口实现自己的方法
	var animal_basic animalBasic // 定一个接口类型的变量
	dog_basic := DogBasic{
		Name: "阿黄",
		Feet: 1,
	}
	// cat_basic := CatBasic{
	// 	Name: "橘子",
	// 	Feet: 3,
	// }
	animal_basic = &dog_basic
	animal_basic.Move() // 阿黄, 狗在动
	res := animal_basic.Say("我在吃狗粮")
	fmt.Println("animal_basic.Say:", res) // 阿黄, 狗在说: 我在吃狗粮
}
